#pragma once
#include <cctype>
#include <string>
#include <vector>

enum COUNTER_TYPE
{
	NUMBERIC,
	ALPHABETIC_LW,//lower case
	ALPHABETIC_UP,//Upper case
	ALPHABETIC_MX,//Mixed case
	ALPHANUMBERIC_LW,//lower alphabet case
	ALPHANUMBERIC_UP,//upper alphabet case
	ALPHANUMBERIC_MX
};

static char numTable[] = "0123456789";
static char abLwTable[] = "abcdefghijklmnopqrstuvwxyz";
static char abUpTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static char abMxTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static char abNumLwTable[] = "0123456789abcdefghijklmnopqrstuvwxyz";
static char abNumUpTable[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static char abNumMxTable[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

int getDataTableIndex(COUNTER_TYPE type, char c);

char* getDataTable(COUNTER_TYPE type);

int getDataTableSize(COUNTER_TYPE type);


class CLetter
{
public:
	CLetter(COUNTER_TYPE type = NUMBERIC):m_type(type)
    {
		m_max = getDataTableSize(m_type) - 1;
        m_table = getDataTable(m_type);
		Reset();
	}


	CLetter(char c,COUNTER_TYPE type = NUMBERIC)
        :Chr(c), m_type(type)
    {
        m_num = getDataTableIndex(m_type, Chr);
		m_max = getDataTableSize(m_type) - 1;
        m_table = getDataTable(m_type);
    }

	void Reset()
	{
		m_num = 0;
		Chr = m_table[m_num];
	}

    //Whether overflow
	bool Tick()
	{
		++m_num;
		if (m_num >= m_max) {
			Reset();
			return true;
		}
        Chr = m_table[m_num];
		return false;
	}

	char Chr;
private:
	unsigned short m_num;
	COUNTER_TYPE m_type;
	int m_max;
    char* m_table;
};

class CCounter
{
public:
	CCounter(COUNTER_TYPE type, int bits, std::string startPos)
		:m_type(type), m_bits(bits), m_start(startPos){

	}

	bool Validate() 
	{
        if (m_start.empty()) 
        {
           for(int i = 0; i < m_bits; ++i) 
           {
               m_letters.push_back(CLetter(m_type));
           }
            return true;
        }

		if ((int)m_start.length() != m_bits)
			return false;

        //"0123456"
        //|6|5|4|3|2|1|0|

        for (std::string::reverse_iterator rit = m_start.rbegin(); rit != m_start.rend(); ++rit)
        {
           char c = *rit; 
            if (!ValidateChar(c , m_type))
              return false;

           m_letters.push_back(CLetter( c, m_type));
        }

		return true;
	}

    static bool ValidateChar(char c, COUNTER_TYPE type)
    {
        switch(type)
        {
            case NUMBERIC:
                return std::isdigit(c);
            case ALPHABETIC_LW:
                return std::isalpha(c) && std::islower(c);
            case ALPHABETIC_UP:
                return std::isalpha(c) && std::isupper(c);
            case ALPHABETIC_MX:
                return std::isalpha(c);
            case ALPHANUMBERIC_LW:
                return std::isdigit(c) || (std::isalpha(c) && std::islower(c));
            case ALPHANUMBERIC_UP:
                return std::isdigit(c) || (std::isalpha(c) && std::isupper(c));
            case ALPHANUMBERIC_MX:
                return std::isalnum(c);
            default:
                return std::isdigit(c);
        }

       return true; 
    }

	//Stoped?
	bool  Tick()
	{
		//z,0
		for (int idx = 0; idx < m_letters.size(); ++idx)
		{
			if (!m_letters[idx].Tick()) {

				if (idx == m_letters.size() - 1) return true; 
				break;
			}
		}
		return false;
	}

	std::string Value()
	{
        std::string str;
        for(std::vector<CLetter>::reverse_iterator rit = m_letters.rbegin();
                    rit != m_letters.rend(); ++rit) 
        {
            CLetter &le = *rit;
            str.push_back(le.Chr);
        }
		
        return str; 
	}
private:
	COUNTER_TYPE m_type;
	int m_bits;
	std::string m_start;

	std::vector<CLetter> m_letters;
};
